summaryrefslogtreecommitdiffstats
path: root/src/core/hid/emulated_controller.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hid/emulated_controller.cpp')
-rw-r--r--src/core/hid/emulated_controller.cpp298
1 files changed, 217 insertions, 81 deletions
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index a959c9db9..6e9812e6e 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
+#include <common/scope_exit.h>
#include "common/polyfill_ranges.h"
#include "common/thread.h"
@@ -94,6 +95,7 @@ void EmulatedController::ReloadFromSettings() {
motion_params[index] = Common::ParamPackage(player.motions[index]);
}
+ controller.color_values = {};
controller.colors_state.fullkey = {
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
@@ -107,6 +109,8 @@ void EmulatedController::ReloadFromSettings() {
.button = GetNpadColor(player.button_color_right),
};
+ ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs);
+
// Other or debug controller should always be a pro controller
if (npad_id_type != NpadIdType::Other) {
SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
@@ -133,18 +137,28 @@ void EmulatedController::LoadDevices() {
trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR];
+ color_params[LeftIndex] = left_joycon;
+ color_params[RightIndex] = right_joycon;
+ color_params[LeftIndex].Set("color", true);
+ color_params[RightIndex].Set("color", true);
+
battery_params[LeftIndex] = left_joycon;
battery_params[RightIndex] = right_joycon;
battery_params[LeftIndex].Set("battery", true);
battery_params[RightIndex].Set("battery", true);
- camera_params = Common::ParamPackage{"engine:camera,camera:1"};
- nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
+ camera_params[0] = right_joycon;
+ camera_params[0].Set("camera", true);
+ camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
+ ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
+ nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
+ nfc_params[1] = right_joycon;
+ nfc_params[1].Set("nfc", true);
output_params[LeftIndex] = left_joycon;
output_params[RightIndex] = right_joycon;
- output_params[2] = camera_params;
- output_params[3] = nfc_params;
+ output_params[2] = camera_params[1];
+ output_params[3] = nfc_params[0];
output_params[LeftIndex].Set("output", true);
output_params[RightIndex].Set("output", true);
output_params[2].Set("output", true);
@@ -160,8 +174,11 @@ void EmulatedController::LoadDevices() {
Common::Input::CreateInputDevice);
std::ranges::transform(battery_params, battery_devices.begin(),
Common::Input::CreateInputDevice);
- camera_devices = Common::Input::CreateInputDevice(camera_params);
- nfc_devices = Common::Input::CreateInputDevice(nfc_params);
+ std::ranges::transform(color_params, color_devices.begin(), Common::Input::CreateInputDevice);
+ std::ranges::transform(camera_params, camera_devices.begin(), Common::Input::CreateInputDevice);
+ std::ranges::transform(ring_params, ring_analog_devices.begin(),
+ Common::Input::CreateInputDevice);
+ std::ranges::transform(nfc_params, nfc_devices.begin(), Common::Input::CreateInputDevice);
std::ranges::transform(output_params, output_devices.begin(),
Common::Input::CreateOutputDevice);
@@ -323,6 +340,19 @@ void EmulatedController::ReloadInput() {
battery_devices[index]->ForceUpdate();
}
+ for (std::size_t index = 0; index < color_devices.size(); ++index) {
+ if (!color_devices[index]) {
+ continue;
+ }
+ color_devices[index]->SetCallback({
+ .on_change =
+ [this, index](const Common::Input::CallbackStatus& callback) {
+ SetColors(callback, index);
+ },
+ });
+ color_devices[index]->ForceUpdate();
+ }
+
for (std::size_t index = 0; index < motion_devices.size(); ++index) {
if (!motion_devices[index]) {
continue;
@@ -336,22 +366,37 @@ void EmulatedController::ReloadInput() {
motion_devices[index]->ForceUpdate();
}
- if (camera_devices) {
- camera_devices->SetCallback({
+ for (std::size_t index = 0; index < camera_devices.size(); ++index) {
+ if (!camera_devices[index]) {
+ continue;
+ }
+ camera_devices[index]->SetCallback({
.on_change =
[this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
});
- camera_devices->ForceUpdate();
+ camera_devices[index]->ForceUpdate();
}
- if (nfc_devices) {
- if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) {
- nfc_devices->SetCallback({
- .on_change =
- [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
- });
- nfc_devices->ForceUpdate();
+ for (std::size_t index = 0; index < ring_analog_devices.size(); ++index) {
+ if (!ring_analog_devices[index]) {
+ continue;
}
+ ring_analog_devices[index]->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
+ });
+ ring_analog_devices[index]->ForceUpdate();
+ }
+
+ for (std::size_t index = 0; index < nfc_devices.size(); ++index) {
+ if (!nfc_devices[index]) {
+ continue;
+ }
+ nfc_devices[index]->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
+ });
+ nfc_devices[index]->ForceUpdate();
}
// Register TAS devices. No need to force update
@@ -421,6 +466,9 @@ void EmulatedController::UnloadInput() {
for (auto& battery : battery_devices) {
battery.reset();
}
+ for (auto& color : color_devices) {
+ color.reset();
+ }
for (auto& output : output_devices) {
output.reset();
}
@@ -436,8 +484,15 @@ void EmulatedController::UnloadInput() {
for (auto& stick : virtual_stick_devices) {
stick.reset();
}
- camera_devices.reset();
- nfc_devices.reset();
+ for (auto& camera : camera_devices) {
+ camera.reset();
+ }
+ for (auto& ring : ring_analog_devices) {
+ ring.reset();
+ }
+ for (auto& nfc : nfc_devices) {
+ nfc.reset();
+ }
}
void EmulatedController::EnableConfiguration() {
@@ -449,6 +504,11 @@ void EmulatedController::EnableConfiguration() {
void EmulatedController::DisableConfiguration() {
is_configuring = false;
+ // Get Joycon colors before turning on the controller
+ for (const auto& color_device : color_devices) {
+ color_device->ForceUpdate();
+ }
+
// Apply temporary npad type to the real controller
if (tmp_npad_type != npad_type) {
if (is_connected) {
@@ -502,6 +562,9 @@ void EmulatedController::SaveCurrentConfig() {
for (std::size_t index = 0; index < player.motions.size(); ++index) {
player.motions[index] = motion_params[index].Serialize();
}
+ if (npad_id_type == NpadIdType::Player1) {
+ Settings::values.ringcon_analogs = ring_params[0].Serialize();
+ }
}
void EmulatedController::RestoreConfig() {
@@ -773,17 +836,21 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (index >= controller.stick_values.size()) {
return;
}
- std::unique_lock lock{mutex};
+ auto trigger_guard =
+ SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); });
+ std::scoped_lock lock{mutex};
const auto stick_value = TransformToStick(callback);
// Only read stick values that have the same uuid or are over the threshold to avoid flapping
if (controller.stick_values[index].uuid != uuid) {
const bool is_tas = uuid == TAS_UUID;
if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) {
+ trigger_guard.Cancel();
return;
}
if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left &&
!stick_value.right) {
+ trigger_guard.Cancel();
return;
}
}
@@ -794,8 +861,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (is_configuring) {
controller.analog_stick_state.left = {};
controller.analog_stick_state.right = {};
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Stick, false);
return;
}
@@ -827,9 +892,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down);
break;
}
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Stick, true);
}
void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback,
@@ -837,7 +899,9 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
if (index >= controller.trigger_values.size()) {
return;
}
- std::unique_lock lock{mutex};
+ auto trigger_guard =
+ SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); });
+ std::scoped_lock lock{mutex};
const auto trigger_value = TransformToTrigger(callback);
// Only read trigger values that have the same uuid or are pressed once
@@ -853,13 +917,12 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
if (is_configuring) {
controller.gc_trigger_state.left = 0;
controller.gc_trigger_state.right = 0;
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Trigger, false);
return;
}
// Only GC controllers have analog triggers
if (npad_type != NpadStyleIndex::GameCube) {
+ trigger_guard.Cancel();
return;
}
@@ -876,9 +939,6 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
controller.npad_button_state.zr.Assign(trigger.pressed.value);
break;
}
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Trigger, true);
}
void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback,
@@ -886,7 +946,8 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
if (index >= controller.motion_values.size()) {
return;
}
- std::unique_lock lock{mutex};
+ SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); });
+ std::scoped_lock lock{mutex};
auto& raw_status = controller.motion_values[index].raw_status;
auto& emulated = controller.motion_values[index].emulated;
@@ -907,8 +968,6 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
force_update_motion = raw_status.force_update;
if (is_configuring) {
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Motion, false);
return;
}
@@ -918,9 +977,56 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
motion.rotation = emulated.GetRotations();
motion.orientation = emulated.GetOrientation();
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
+}
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Motion, true);
+void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback,
+ std::size_t index) {
+ if (index >= controller.color_values.size()) {
+ return;
+ }
+ auto trigger_guard =
+ SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); });
+ std::scoped_lock lock{mutex};
+ controller.color_values[index] = TransformToColor(callback);
+
+ if (is_configuring) {
+ return;
+ }
+
+ if (controller.color_values[index].body == 0) {
+ trigger_guard.Cancel();
+ return;
+ }
+
+ controller.colors_state.fullkey = {
+ .body = GetNpadColor(controller.color_values[index].body),
+ .button = GetNpadColor(controller.color_values[index].buttons),
+ };
+ if (npad_type == NpadStyleIndex::ProController) {
+ controller.colors_state.left = {
+ .body = GetNpadColor(controller.color_values[index].left_grip),
+ .button = GetNpadColor(controller.color_values[index].buttons),
+ };
+ controller.colors_state.right = {
+ .body = GetNpadColor(controller.color_values[index].right_grip),
+ .button = GetNpadColor(controller.color_values[index].buttons),
+ };
+ } else {
+ switch (index) {
+ case LeftIndex:
+ controller.colors_state.left = {
+ .body = GetNpadColor(controller.color_values[index].body),
+ .button = GetNpadColor(controller.color_values[index].buttons),
+ };
+ break;
+ case RightIndex:
+ controller.colors_state.right = {
+ .body = GetNpadColor(controller.color_values[index].body),
+ .button = GetNpadColor(controller.color_values[index].buttons),
+ };
+ break;
+ }
+ }
}
void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
@@ -928,12 +1034,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
if (index >= controller.battery_values.size()) {
return;
}
- std::unique_lock lock{mutex};
+ SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); });
+ std::scoped_lock lock{mutex};
controller.battery_values[index] = TransformToBattery(callback);
if (is_configuring) {
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Battery, false);
return;
}
@@ -989,18 +1094,14 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
};
break;
}
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Battery, true);
}
void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
- std::unique_lock lock{mutex};
+ SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); });
+ std::scoped_lock lock{mutex};
controller.camera_values = TransformToCamera(callback);
if (is_configuring) {
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::IrSensor, false);
return;
}
@@ -1008,18 +1109,28 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback
controller.camera_state.format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
controller.camera_state.data = controller.camera_values.data;
+}
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::IrSensor, true);
+void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
+ SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); });
+ std::scoped_lock lock{mutex};
+ const auto force_value = TransformToStick(callback);
+
+ controller.ring_analog_value = force_value.x;
+
+ if (is_configuring) {
+ return;
+ }
+
+ controller.ring_analog_state.force = force_value.x.value;
}
void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
- std::unique_lock lock{mutex};
+ SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); });
+ std::scoped_lock lock{mutex};
controller.nfc_values = TransformToNfc(callback);
if (is_configuring) {
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Nfc, false);
return;
}
@@ -1027,9 +1138,6 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
controller.nfc_values.state,
controller.nfc_values.data,
};
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Nfc, true);
}
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
@@ -1061,7 +1169,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
.type = type,
};
return output_devices[device_index]->SetVibration(status) ==
- Common::Input::VibrationError::None;
+ Common::Input::DriverResult::Success;
}
bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
@@ -1083,16 +1191,32 @@ bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
return output_devices[device_index]->IsVibrationEnabled();
}
-bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
- LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
- auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
+Common::Input::DriverResult EmulatedController::SetPollingMode(
+ EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) {
+ LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index);
+
+ auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)];
+ auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
auto& nfc_output_device = output_devices[3];
- const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
- const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode);
+ if (device_index == EmulatedDeviceIndex::LeftIndex) {
+ return left_output_device->SetPollingMode(polling_mode);
+ }
+
+ if (device_index == EmulatedDeviceIndex::RightIndex) {
+ const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
+ const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode);
+
+ if (virtual_nfc_result == Common::Input::DriverResult::Success) {
+ return virtual_nfc_result;
+ }
+ return mapped_nfc_result;
+ }
- return virtual_nfc_result == Common::Input::PollingError::None ||
- mapped_nfc_result == Common::Input::PollingError::None;
+ left_output_device->SetPollingMode(polling_mode);
+ right_output_device->SetPollingMode(polling_mode);
+ nfc_output_device->SetPollingMode(polling_mode);
+ return Common::Input::DriverResult::Success;
}
bool EmulatedController::SetCameraFormat(
@@ -1103,13 +1227,22 @@ bool EmulatedController::SetCameraFormat(
auto& camera_output_device = output_devices[2];
if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
- camera_format)) == Common::Input::CameraError::None) {
+ camera_format)) == Common::Input::DriverResult::Success) {
return true;
}
// Fallback to Qt camera if native device doesn't have support
return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
- camera_format)) == Common::Input::CameraError::None;
+ camera_format)) == Common::Input::DriverResult::Success;
+}
+
+Common::ParamPackage EmulatedController::GetRingParam() const {
+ return ring_params[0];
+}
+
+void EmulatedController::SetRingParam(Common::ParamPackage param) {
+ ring_params[0] = std::move(param);
+ ReloadInput();
}
bool EmulatedController::HasNfc() const {
@@ -1263,39 +1396,35 @@ void EmulatedController::Connect(bool use_temporary_value) {
return;
}
- std::unique_lock lock{mutex};
+ auto trigger_guard =
+ SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); });
+ std::scoped_lock lock{mutex};
if (is_configuring) {
tmp_is_connected = true;
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Connected, false);
return;
}
if (is_connected) {
+ trigger_guard.Cancel();
return;
}
is_connected = true;
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Connected, true);
}
void EmulatedController::Disconnect() {
- std::unique_lock lock{mutex};
+ auto trigger_guard =
+ SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); });
+ std::scoped_lock lock{mutex};
if (is_configuring) {
tmp_is_connected = false;
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Disconnected, false);
return;
}
if (!is_connected) {
+ trigger_guard.Cancel();
return;
}
is_connected = false;
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Disconnected, true);
}
bool EmulatedController::IsConnected(bool get_temporary_value) const {
@@ -1320,19 +1449,21 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
}
void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
- std::unique_lock lock{mutex};
+ auto trigger_guard =
+ SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); });
+ std::scoped_lock lock{mutex};
if (is_configuring) {
if (tmp_npad_type == npad_type_) {
+ trigger_guard.Cancel();
return;
}
tmp_npad_type = npad_type_;
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Type, false);
return;
}
if (npad_type == npad_type_) {
+ trigger_guard.Cancel();
return;
}
if (is_connected) {
@@ -1340,9 +1471,6 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
NpadIdTypeToIndex(npad_id_type));
}
npad_type = npad_type_;
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Type, true);
}
LedPattern EmulatedController::GetLedPattern() const {
@@ -1403,6 +1531,10 @@ CameraValues EmulatedController::GetCameraValues() const {
return controller.camera_values;
}
+RingAnalogValue EmulatedController::GetRingSensorValues() const {
+ return controller.ring_analog_value;
+}
+
HomeButtonState EmulatedController::GetHomeButtons() const {
std::scoped_lock lock{mutex};
if (is_configuring) {
@@ -1436,7 +1568,7 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
}
AnalogSticks EmulatedController::GetSticks() const {
- std::unique_lock lock{mutex};
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
@@ -1486,6 +1618,10 @@ const CameraState& EmulatedController::GetCamera() const {
return controller.camera_state;
}
+RingSensorForce EmulatedController::GetRingSensorForce() const {
+ return controller.ring_analog_state;
+}
+
const NfcState& EmulatedController::GetNfc() const {
std::scoped_lock lock{mutex};
return controller.nfc_state;